let activeEffect = undefined;

const bucket = new WeakMap();
// 追踪
const track = (target, key) => {
  if (!activeEffect) return;

  let depsMap = bucket.get(target); // WeakMap 由 target --> Map 构成；
  if (!depsMap) {
    bucket.set(target, (depsMap = new Map()));
  }
  let deps = depsMap.get(key); // Map 由 key --> Set 构成
  if (!deps) {
    depsMap.set(key, (deps = new Set()));
  }
  // 最后将当前激活的副作用函数添加到桶中
  deps.add(activeEffect);

  // deps就是一个与当前副作用函数存在关联的依赖集合
  activeEffect.deps.push(deps);
};
// 触发
const trigger = (target, key) => {
  let depsMap = bucket.get(target);
  if (!depsMap) return;
  let deps = depsMap.get(key);
  // 执行副作用函数
  const effectSet = new Set(deps);
  // 【4.6】在trigger动作发生时 增加守卫条件：如果trigger触发的副作用函数与当前正在执行的副作用函数是同一个 则不触发执行
  //  effectSet && effectSet.forEach((fn) => fn());
  const effectSetRun = new Set();
  effectSet &&
    effectSet.forEach((fn) => {
      if (fn !== activeEffect) {
        effectSetRun.add(fn);
      }
    });
  effectSetRun && effectSetRun.forEach((fn) => fn());
};

// 定义一个栈来存储副作用函数
const effectStack = [];
const effect = (fn) => {
  const _effectFn = () => {
    cleanup(_effectFn);
    activeEffect = _effectFn;
    // 将当前副作用函数压入栈中
    effectStack.push(_effectFn);
    fn();
    // 副作用函数执行完之后 将其从栈中弹出
    effectStack.pop();
    // activeEffect始终指向栈顶的副作用函数
    activeEffect = effectStack[effectStack.length - 1];
  };
  //  用来存储 所有与当前副作用函数相关联的依赖集合
  _effectFn.deps = [];

  _effectFn();
};

const cleanup = (effectFn) => {
  for (let i = 0; i < effectFn.deps.length; i++) {
    const deps = effectFn.deps[i];
    deps.delete(effectFn);
  }
  effectFn.deps.length = 0;
};

// 4.6 避免无限递归调用

// 测试代码
const data = {
  foo: 1,
};
const obj = new Proxy(data, {
  get(target, key) {
    track(target, key);
    return target[key];
  },
  set(target, key, val) {
    target[key] = val;
    trigger(target, key);
    return true;
  },
});

effect(() => {
  obj.foo = obj.foo + 1;
  console.log(obj.foo);
});
// 如上：会有栈溢出的报错 Maximum call stack size exceeded
// 流程：读取obj.foo的值触发track函数，把当前副作用函数收集到桶中，然后再次读取obj.foo的值，触发trigger函数，把桶中副作用函数取出并执行。
// 但问题是该副作用函数正在执行中，还没有执行完毕，就要开始下一次的执行，会导致无限递归调用自己，导致栈溢出
